package fr.laas.knxinterface.communication;

import java.util.ArrayList;

import fr.laas.knxinterface.commands.Commands;

/**
 * Allows to handle the messages received from the gateway, translate them into
 * the right command and send the command
 * 
 * @author Guillaume Garzone
 * 
 */
public class MessageHandler extends Thread {

	// true when the thread is started
	boolean running = false;

	// delay of interruption of the thread in case of an empty buffer
	long delay = 100000; // ms

	// allows to store the message waiting for processing
	private ArrayList<String> buffer;

	// regular expressions to check the format of the messages received

	// group adress expression : 0..15/0...12/0..255
	private String regExprGad1 = "([0-9]|(1[0-5]))"; // 0..15
	private String regExprGad2 = "([0-9]|(1[0-2]))"; // 0..12
	private String regExprGad3 = "((2[0-4][0-9]|25[0-5])|([0-1]?[0-9]?[0-9]))"; // 0..255
	private String regExprGad = regExprGad1 + "\\/" + regExprGad2 + "\\/"
			+ regExprGad3; // 0..15/0..12/0..255
	// commands expressions
	private String regExprValue = ".+";
	private String regExprWrite = "write " + regExprGad + ": " + regExprValue; // write
																				// X/X/X:
																				// on
																				// OR
																				// write
																				// X/X/X:
																				// off
	private String regExprRead = "read " + regExprGad; // read X/X/X

	// allows to send the READ or WRITE commands received from the gateway
	private Commands commands;

	/**
	 * Constructor
	 */
	public MessageHandler() {
		this.buffer = new ArrayList<String>();
		this.commands = new Commands();

	}

	/**
	 * Allows to add a message to the buffer of the handler (FIFO)
	 * 
	 * @param message
	 */
	public void addMessageBuffer(String message) {

		// adds the message to the buffer (protected against concurrent access)
		synchronized (this.buffer) {
			buffer.add(message);
		}
		// if the thread is sleeping, awakes it
		this.interrupt();
	}

	/**
	 * Allows to get the first message in the buffer and deletes it from the
	 * buffer
	 * 
	 * @return null if buffer is empty
	 */
	public String getFirstMessage() {

		String message = null;

		synchronized (this.buffer) {
			if (this.buffer.size() != 0) {
				message = this.buffer.get(0);
				this.buffer.remove(0);
			}
		}
		return message;

	}

	/**
	 * Processes a write command - command is formated correctly
	 * 
	 * @param command
	 */
	public void processWrite(String command) {

		// System.out.println("DEBUG Je vais exécuter ça : "+command) ;

		int i = 0;
		String[] results;
		// boolean value ;

		// deleting "Write "
		i = command.indexOf(" ");
		command = command.substring(i + 1);

		// splitting the string to get the group adress and the value
		results = command.split(": ");
		// deleting the " " from the group adress
		results[0].replaceAll(" ", "");
		// converting value to boolean
		// value = results[1].matches("on") ;

		// sending the command to the bus KNX
		this.commands.write(results[0], results[1]);

	}

	/**
	 * Processes a read command - command is formated correctly
	 * 
	 * @param command
	 */
	public void processRead(String command) {

		int i = 0;

		// deleting "Read " from the string
		i = command.indexOf(" ");
		command = command.substring(i + 1);

		// sending the command with the group adress
		this.commands.read(command);

	}

	/**
	 * Allows to call the corrects commands regarding to the message received
	 * 
	 * @param message
	 */
	public void processMessage(String message) {
		/*---------------------------------------
		 * 
		 * CORRECT FORMAT OF MESSAGE RECEIVED
		 * 
		 * write X/X/XX: 01
		 * write X/X/XX: 00
		 * read X/X/XX
		 * close
		 * 
		 *--------------------------------------
		 */

		message = message.toLowerCase();

		// System.out.println("Je traite le message") ;
		if (message.matches(this.regExprWrite)) {
			// received a write command, good format
			System.out.println("HANDLER Processing: " + message);
			this.processWrite(message);

		} else if (message.matches(this.regExprRead)) {
			// received a write command, good format
			System.out.println("HANDLER Processing: " + message);
			this.processRead(message);

		} else {
			// message received is incorrect or corrupted
			System.err.println("Received a non recognized message");
		}

	}

	/**
	 * Stops the thread correctly
	 */
	public void stopHandler() {
		this.running = false;
	}

	/**
	 * The threads checks periodically the buffer and processes the messages
	 * received.
	 */
	@Override
	public void run() {

		String message = null;

		this.running = true;

		while (this.running) {

			// gets the first message received
			message = this.getFirstMessage();

			if (message != null) {
				// the handler received at least one message
				System.out.println("HANDLER received: " + message);
				// processing the message
				this.processMessage(message);

			} else {

				// the handler has not received any message
				try {
					// suspends the execution of the thread for a while, waiting
					// for messages
					Thread.sleep(this.delay);
				} catch (InterruptedException e) {
					// A new message has been received
				}

			}

		}
	}
}